<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>11.仿射变换的应用：实现粒子动画</title>
    <style>
        canvas{
            border:1px solid #aaa;
            margin: 0 auto;
            display: block;
        }
    </style>
</head>
<body>
    <canvas width="800" height="800"></canvas>

    <script type="module">
        // 导入进行三角剖分的库
        import { Vector2D } from '../common/lib/utils.js';

        
        const canvas = document.querySelector("canvas")
        const gl = canvas.getContext("webgl")

        const vertex = `
            attribute vec2 position;

            uniform float u_rotation;
            uniform float u_time;
            uniform float u_duration;
            uniform float u_scale;
            uniform vec2 u_dir;

            varying float vP;

            void main() {
                float p = min(1.0, u_time / u_duration);
                float rad = u_rotation + 3.14 * 10.0 * p;
                float scale = u_scale * p * (2.0 - p);
                vec2 offset = 2.0 * u_dir * p * p;
                mat3 translateMatrix = mat3(
                    1.0, 0.0, 0.0,
                    0.0, 1.0, 0.0,
                    offset.x, offset.y, 1.0
                );
                mat3 rotateMatrix = mat3(
                    cos(rad), -sin(rad), 0.0,
                    sin(rad), cos(rad),  0.0,
                    0.0,      0.0,       1.0
                );
                mat3 scaleMatrix = mat3(
                    scale, 0.0, 0.0,
                    0.0, scale, 0.0,
                    0.0, 0.0, 1.0
                );
                gl_PointSize = 1.0;
                vec3 pos = translateMatrix * rotateMatrix * scaleMatrix * vec3(position, 1.0);
                gl_Position = vec4(pos, 1.0);
                vP = p;
            }
        `;

        const fragment = `
            precision mediump float;
            uniform vec4 u_color;
            varying float vP;

            void main()
            {
                gl_FragColor.xyz = u_color.xyz;
                gl_FragColor.a = (1.0 - vP) * u_color.a;
            }
        `;
        
        // 创建顶点着色器对象
        const vertexShader = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(vertexShader, vertex);
        gl.compileShader(vertexShader);

        // 创建片元着色器对象
        const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
        gl.shaderSource(fragmentShader, fragment);
        gl.compileShader(fragmentShader);

        // 2. 创建 WebGLProgram 对象
        const program = gl.createProgram();
        // 添加 vertexShader 和 fragmentShader
        gl.attachShader(program, vertexShader);
        gl.attachShader(program, fragmentShader);

        // 将这个 WebGLProgram 对象链接到 WebGL 上下文对象上
        gl.linkProgram(program);

        // 通过 useProgram 选择启用这个 WebGLProgram 对象，这样，当我们绘制图形时，GPU 就会执行我们通过 WebGLProgram 设定的 两个 shader 程序了。
        gl.useProgram(program);

        // 3.将顶点数组存入缓存区
        const position = new Float32Array([ -1, -1, 0, 1, 1, -1,]);

        const pointBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, pointBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, position, gl.STATIC_DRAW);

        // 4. 将缓冲区数据读取到 GPU
        // 把数据绑定给顶点着色器中的 position 变量。
        // 获取顶点着色器中的position变量的地址
        const vPosition = gl.getAttribLocation(program, 'position');
        // 给变量设置长度和类型
        gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);
        // 激活这个变量
        gl.enableVertexAttribArray(vPosition);

        // 创建三角形
        // gl.clear(gl.COLOR_BUFFER_BIT);
        // gl.drawArrays(gl.TRIANGLES, 0, position.length/2); // 三角形

        // 5. 创建随机三角形属性的函数
        function randomTriangles() {
            const u_color = [Math.random(), Math.random(), Math.random(), 1.0]; // 随机颜色
            const u_rotation = Math.random() * Math.PI; // 初始旋转角度
            const u_scale = Math.random() * 0.05 + 0.03; // 初始大小
            const u_time = 0;
            const u_duration = 3.0; // 持续3秒钟

            const rad = Math.random() * Math.PI * 2;
            const u_dir = [Math.cos(rad), Math.sin(rad)]; // 运动方向
            const startTime = performance.now();

            return {u_color, u_rotation, u_scale, u_time, u_duration, u_dir, startTime};
        }


        // 6. 设置 uniform 变量
        function setUniforms(gl, {u_color, u_rotation, u_scale, u_time, u_duration, u_dir}) {
            // gl.getUniformLocation 拿到uniform变量的指针
            let loc = gl.getUniformLocation(program, 'u_color');
            // 将数据传给 unfirom 变量的地址
            gl.uniform4fv(loc, u_color);

            loc = gl.getUniformLocation(program, 'u_rotation');
            gl.uniform1f(loc, u_rotation);

            loc = gl.getUniformLocation(program, 'u_scale');
            gl.uniform1f(loc, u_scale);

            loc = gl.getUniformLocation(program, 'u_time');
            gl.uniform1f(loc, u_time);

            loc = gl.getUniformLocation(program, 'u_duration');
            gl.uniform1f(loc, u_duration);

            loc = gl.getUniformLocation(program, 'u_dir');
            gl.uniform2fv(loc, u_dir);
        }

        // 7. 用 requestAnimationFrame 实现动画
        
        let triangles = [];// 表示三角形数量的集合

        function update() {
            console.log(triangles)
            // 新建数个随机三角形
            for(let i = 0; i < 5 * Math.random(); i++) {
                // 存储每个三角形的属性
                triangles.push(randomTriangles());
            }
            // 清空缓冲
            gl.clear(gl.COLOR_BUFFER_BIT);
            // 对每个三角形重新设置u_time
            triangles.forEach((triangle) => {
                triangle.u_time = (performance.now() - triangle.startTime) / 1000;
                // 将每个三角形的属性传递给 顶点着色器和片元着色器
                setUniforms(gl, triangle);
                // 绘制三角形
                gl.drawArrays(gl.TRIANGLES, 0, position.length / 2);
            });
            // 移除已经结束动画的三角形
            triangles = triangles.filter((triangle) => {
                return triangle.u_time <= triangle.u_duration;
            });
            requestAnimationFrame(update);
        }
        requestAnimationFrame(update);

    </script>
</body>
</html>